﻿using TestingConcurrencyBenchmark.Helpers;

namespace TestingConcurrencyBenchmark.Problems.CigaretteSmokers
{
    public class CigaretteSmokerSolution : AbstractCigaretteSmoker
    {
        private readonly BenchmarkSemaphore _pusherMutex = new BenchmarkSemaphore(1);
        private readonly BenchmarkSemaphore _smokerWithMatch = new BenchmarkSemaphore(0);
        private readonly BenchmarkSemaphore _smokerWithPaper = new BenchmarkSemaphore(0);
        private readonly BenchmarkSemaphore _smokerWithTobacco = new BenchmarkSemaphore(0);

        private bool _isMatch;
        private bool _isPaper;
        private bool _isTobacco;

        #region pushers

        public override void PushTobacco()
        {
            CreatedTobacco.WaitOne();
            _pusherMutex.WaitOne();
            if (_isPaper)
            {
                //Paper and tobacco => Signal the smoker with the match
                _isPaper = false;
                _smokerWithMatch.Release();
            }
            else
            {
                if (_isMatch)
                {
                    //Match and tobacco => Signal the smoker with paper
                    _isMatch = false;
                    _smokerWithPaper.Release();
                }
                else
                {
                    //Just tobacco, no other ingrediënts received yet
                    _isTobacco = true;
                }
            }
            _pusherMutex.Release();
        }

        public override void PushPaper()
        {
            CreatedPaper.WaitOne();
            _pusherMutex.WaitOne();
            if (_isTobacco)
            {
                //paper and tobacco => Signal the smoker with the match
                _isTobacco = false;
                _smokerWithMatch.Release();
            }
            else
            {
                if (_isMatch)
                {
                    //paper and match => Signal the smoker with tobacco
                    _isMatch = false;
                    _smokerWithTobacco.Release();
                }
                else
                {
                    //Just paper, no other ingrediënts received yet
                    _isPaper = true;
                }
            }
            _pusherMutex.Release();
        }

        public override void PushMatch()
        {
            CreatedMatch.WaitOne();
            _pusherMutex.WaitOne();
            if (_isTobacco)
            {
                //tobacco and match => Signal the smoker with paper
                _isTobacco = false;
                _smokerWithPaper.Release();
            }
            else
            {
                if (_isPaper)
                {
                    //paper and match => Signal the smoker with tobacco
                    _isPaper = false;
                    _smokerWithTobacco.Release();
                }
                else
                {
                    //Just match, no other ingrediënts received yet
                    _isMatch = true;
                }
            }
            _pusherMutex.Release();
        }

        #endregion

        #region smokers

        public override void ConsumeResourcesAsSmokerWithMatch()
        {
            _smokerWithMatch.WaitOne();
            AgentLock.Release();
        }

        public override void ConsumeResourcesAsSmokerWithTobacco()
        {
            _smokerWithTobacco.WaitOne();
            AgentLock.Release();
        }

        public override void ConsumeResourcesAsSmokerWithPaper()
        {
            _smokerWithPaper.WaitOne();
            AgentLock.Release();
        }

        #endregion
    }
}